/****************************************************************************
*                                                                           *
* mainwindow.cpp								                            *
*                                                                           *
* ALaRI 2008												                *
*                                                                           *
****************************************************************************/

/****************************************************************************
* Includes                                                                  *
****************************************************************************/
#include "mainwindow.h"
#include "../../core/prop/matrepository.h"
#include "../../core/prop/sensorrepository.h"
#include "../../core/prop/sensor.h"
#include "../../core/prop/sensorgraph.h"

#include <QList>
#include <cstring>
#include <QMessageBox>
#include <QProgressDialog>
#include <QString>
#include <QFileDialog>

/****************************************************************************
* CMainWindow - main window of the application					 			*
****************************************************************************/
CMainWindow::CMainWindow(const char *name /*= 0*/, Qt::WindowFlags flags /* = 0*/) :
	QMainWindow(0, flags), m_pPropagationSim(NULL)
{
	setWindowTitle(name == NULL ? "" : tr(name));
	initSimulator();
	createDialogs();
	createActions();
	createMenus();
	createToolbars();
	createDockWidgets();
	createMainWindow();
}

CMainWindow::~CMainWindow()
{
	SafeFree(m_pPropagationSim);
}

void CMainWindow::createDialogs()
{
	m_pProgressDlg = new QProgressDialog(tr("Simulating..."), tr("Cancel"), 0, 0, this);
	m_pProgressDlg->setModal(true);
	m_pProgressDlg->setAutoClose(true);
	connect(m_pPropagationSim, SIGNAL(doing(QString)), m_pProgressDlg, SLOT(setLabelText(QString))); 
	connect(m_pProgressDlg, SIGNAL(canceled()), this, SLOT(cancelSimulation())); 

	m_pSettingsDialog = new CSettingsDialog(CSimProperty::inst(), this);
	m_pDrawSpecialDialog = new CDrawDialog(this);

}

void CMainWindow::initSimulator()
{
	// init simulation properties
	CSimProperty::inst();
	// init materials 
	CMaterialRepository::inst()->init();
	// init Sensors
	CSensorRepository::inst()->init();

	// create simmulator
	m_pPropagationSim = new CScatSimulator(m_qWidgetFloor.getWidthR(), m_qWidgetFloor.getHeightR(), m_qImageFloor);
	m_qWidgetFloor.setAirColor(CMaterialRepository::inst()->getMaterial(tr("Air"))->color());
	m_qWidgetSim.resize(m_qWidgetFloor.getWidth(), m_qWidgetFloor.getHeight());
	connect(m_pPropagationSim, SIGNAL(simulationDone()), this, SLOT(finishSimulation()));
}

void CMainWindow::createDockWidgets()
{
	QDockWidget *pDockMaterial = new QDockWidget(tr("Materials"), this);
	pDockMaterial->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

	m_pQListWidgetMaterials = new QListWidget(pDockMaterial);
	m_pQListWidgetMaterials->addItems(CMaterialRepository::inst()->getQListNames());

	pDockMaterial->setWidget(m_pQListWidgetMaterials);
	addDockWidget(Qt::RightDockWidgetArea, pDockMaterial);
	m_pQMenuView->addAction(pDockMaterial->toggleViewAction());

	QDockWidget *pDockSensors = new QDockWidget(tr("Sensors"), this);
	pDockSensors->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

	m_pQListWidgetSensors = new QListWidget(pDockSensors);
	connect(m_pQListWidgetSensors, SIGNAL(itemClicked(QListWidgetItem*)), 
		this, SLOT(selectSensor(QListWidgetItem *)));
	connect(CSensorRepository::inst(), SIGNAL(changed()), this, SLOT(updateSensorList()));
	
	pDockSensors->setWidget(m_pQListWidgetSensors);
	addDockWidget(Qt::RightDockWidgetArea, pDockSensors);
	m_pQMenuView->addAction(pDockSensors->toggleViewAction());

}

void CMainWindow::createToolbars()
{
	m_pCreateToolbar = addToolBar(tr("Open"));
	m_pCreateToolbar->addAction(m_pActionOpen);
	m_pCreateToolbar->addAction(m_pActionSave);

	m_pPenWidthSpinBox = new QSpinBox();
	m_pPenWidthSpinBox->setRange(1, 50);
	m_pPenWidthSpinBox->setToolTip("pen width");
	connect(m_pPenWidthSpinBox, SIGNAL(valueChanged(int)), &m_qWidgetFloor, SLOT(setPenWidth(int))); 
	m_pPenWidthSpinBox->setValue(5);

	m_pMaterialComboBox = new QComboBox();
	m_pMaterialComboBox->addItems(CMaterialRepository::inst()->getQListNames()); 
	connect(m_pMaterialComboBox, SIGNAL(currentIndexChanged(const QString&)), 
		this, SLOT(selectMaterial(const QString&)));
	m_pMaterialComboBox->setCurrentIndex(1);

	m_pDrawToolbar = addToolBar(tr("Edit"));
	m_pDrawToolbar->addAction(m_pActionNewFloor);
	m_pDrawToolbar->addAction(m_pActionIdle);
	m_pDrawToolbar->addAction(QIcon(":/resources/images/floorplan.png"), tr("&Demo"), 
		&m_qWidgetFloor, SLOT(drawDemo())); 
	m_pDrawToolbar->addAction(m_pActionDrawWall);
	m_pDrawToolbar->addAction(m_pActionDrawRoom);
	m_pDrawToolbar->addAction(m_pActionDrawRound);
	m_pDrawToolbar->addAction(m_pActionDrawSpecial);
	m_pDrawToolbar->addAction(QIcon(":/resources/images/erase.png"), tr("&Delete"),
		&m_qWidgetFloor, SLOT(deleteCurrentShape()));
	m_pDrawToolbar->addSeparator();
	m_pDrawToolbar->addWidget(m_pPenWidthSpinBox);
	m_pDrawToolbar->addSeparator();
	m_pDrawToolbar->addWidget(m_pMaterialComboBox);

	m_pSimToolbar = addToolBar(tr("Simulation"));
	m_pSimToolbar->addAction(m_pActionPutSensor);
	m_pSimToolbar->addAction(m_pQActionSimulate);
}

void CMainWindow::createMainWindow()
{
	QScrollArea *pScrollAreaFloorPlan = new QScrollArea();
	pScrollAreaFloorPlan->setAlignment(Qt::AlignCenter);
	pScrollAreaFloorPlan->setAutoFillBackground(true);
	pScrollAreaFloorPlan->setWidget(&m_qWidgetFloor);

	QScrollArea *pScrollAreaSimulation = new QScrollArea();
	pScrollAreaSimulation->setAlignment(Qt::AlignCenter);
	pScrollAreaSimulation->setBackgroundRole(QPalette::Dark);
	pScrollAreaSimulation->setAutoFillBackground(true);
	pScrollAreaSimulation->setWidget(&m_qWidgetSim);

//	m_qLabelSim.setVisible(false);

	QGridLayout *pGridLayoutSigProp = new QGridLayout();
	pGridLayoutSigProp->addWidget(pScrollAreaFloorPlan, 0, 0, 1, 1);
	pGridLayoutSigProp->addWidget(pScrollAreaSimulation, 1, 0, 1, 1);

	QWidget *pWidgetSigProp = new QWidget();
	pWidgetSigProp->setBackgroundRole(QPalette::Background);
	pWidgetSigProp->setAutoFillBackground(true);
	pWidgetSigProp->setLayout(pGridLayoutSigProp);

	QWidget *pWidgetNetwork = new QWidget();

	m_pTabs = new QTabWidget(this);
	m_pTabs->addTab(pWidgetSigProp, tr("Signal Propagation"));
	m_pTabs->addTab(pWidgetNetwork, tr("Network"));

	m_pTabs->setTabToolTip(0, tr("Simulates signal propagation \nin an indoor enviroument"));

	setCentralWidget(m_pTabs);
}

void CMainWindow::selectMaterial(const QString& qStrName)
{
	m_qWidgetFloor.setPenColor(CMaterialRepository::inst()->getMaterial(qStrName)->color());
}

void CMainWindow::closeEvent(QCloseEvent *event)
{
	SafeFree(m_pPropagationSim);
	CMaterialRepository::inst()->destroy();
	CSensorRepository::inst()->destroy();
}

void CMainWindow::createMenus()
{
	m_pQMenuFile = menuBar()->addMenu(tr("&File"));
	m_pQMenuFile->addAction(m_pActionNewFloor);

	m_pQMenuView = menuBar()->addMenu(tr("&View"));

	m_pQMenuSimulate = menuBar()->addMenu(tr("&Simulation"));
	m_pQMenuSimulate->addAction(m_pQActionSimulate);
	m_pQMenuSimulate->addAction(m_pQActionExportGraphToGraphViz);
	m_pQMenuSimulate->addAction(m_pQActionSettings);


	m_pQMenuGeometry = menuBar()->addMenu(tr("&Geometry"));
	m_pQMenuGeometry->addActions(m_pActionGroupDraw->actions());
	m_pQMenuGeometry->addAction(m_pActionDrawSpecial);

}

void CMainWindow::createActions()
{
	// create application actions
	// new 
	m_pActionNewFloor  = new QAction(tr("&New"), this);
	m_pActionNewFloor->setShortcut(QString("Ctrl+N"));
	m_pActionNewFloor->setIcon(QIcon(":/resources/images/filenew.png"));
	connect(m_pActionNewFloor, SIGNAL(triggered()), this, SLOT(createNewFloorPlan())); 
	// open
	m_pActionOpen  = new QAction(tr("&New"), this);
	m_pActionOpen->setShortcut(QString("Ctrl+O"));
	m_pActionOpen->setIcon(QIcon(":/resources/images/fileopen.png"));
	// connect()
	// save
	m_pActionSave  = new QAction(tr("&New"), this);
	m_pActionSave->setShortcut(QString("Ctrl+S"));
	m_pActionSave->setIcon(QIcon(":/resources/images/filesave.png"));
	// connect()
	// close
	m_pActionClose = new QAction(tr("E&xit"), this);
	m_pActionClose->setShortcut(QString("Ctrl+X"));
    connect(m_pActionClose, SIGNAL(triggered()), this, SLOT(closeApplication()));

	// create drawing actions
	// draw wall
	m_pActionDrawWall = new QAction(tr("New &Wall"), this);
	m_pActionDrawWall->setCheckable(true);
	m_pActionDrawWall->setIcon(QIcon(":/resources/images/wall.png"));
	connect(m_pActionDrawWall, SIGNAL(triggered()), &m_qWidgetFloor, SLOT(drawWall())); 
	// draw room
	m_pActionDrawRoom = new QAction(tr("New &Room"), this);
	m_pActionDrawRoom->setCheckable(true);
	m_pActionDrawRoom->setIcon(QIcon(":/resources/images/room.png"));
	connect(m_pActionDrawRoom, SIGNAL(triggered()), &m_qWidgetFloor, SLOT(drawRoom())); 
	// draw round room
	m_pActionDrawRound = new QAction(tr("New &Round Room"), this);
	m_pActionDrawRound->setCheckable(true);
	m_pActionDrawRound->setIcon(QIcon(":/resources/images/round.png"));
	connect(m_pActionDrawRound, SIGNAL(triggered()), &m_qWidgetFloor, SLOT(drawRoundRoom())); 
	//draw special 
	m_pActionDrawSpecial = new QAction(tr("Draw Special"), this);
	connect(m_pActionDrawSpecial, SIGNAL(triggered()), this, SLOT(onDrawSpecial())); 
	m_pActionDrawSpecial->setIcon(QIcon(":/resources/images/drawspecial.png"));

	// idle
	m_pActionIdle = new QAction(tr("&Idle"), this);
	m_pActionIdle->setCheckable(true);
	m_pActionIdle->setIcon(QIcon(":/resources/images/pointer.png"));
	m_pActionIdle->setChecked(true);
	connect(m_pActionIdle, SIGNAL(triggered()), &m_qWidgetFloor, SLOT(setIdle())); 
	// put sensor
	m_pActionPutSensor = new QAction(tr("&Add sensor"), this);
	m_pActionPutSensor->setCheckable(true);
	m_pActionPutSensor->setIcon(QIcon(":/resources/images/sensor.png"));
	connect(m_pActionPutSensor, SIGNAL(triggered()), &m_qWidgetFloor, SLOT(putSensor())); 
	// create simulation actions
	// simulate 
	m_pQActionSimulate = new QAction(tr("&Simulate Signal Propagation"), this);
	m_pQActionSimulate->setIcon(QIcon(":/resources/images/simulate.png"));
	connect(m_pQActionSimulate, SIGNAL(triggered()), this, SLOT(startSimulation())); 

	// create settings actions
	m_pQActionSettings = new QAction(tr("&Settings"), this);
	connect(m_pQActionSettings, SIGNAL(triggered()), this, SLOT(onSettings())); 

	m_pQActionExportGraphToGraphViz = new QAction(tr("&Export Network Graph to GraphViz"), this);
	m_pQActionExportGraphToGraphViz->setIcon(QIcon(":/resources/images/graphviz-icon.png"));
	m_pQActionExportGraphToGraphViz->setEnabled(false); 
	connect(m_pQActionExportGraphToGraphViz, SIGNAL(triggered()), this, SLOT(exportGraphToGraphViz())); 

	// create a group
	m_pActionGroupDraw = new QActionGroup(this);
	m_pActionGroupDraw->setExclusive(true);
	m_pActionGroupDraw->addAction(m_pActionDrawWall);
	m_pActionGroupDraw->addAction(m_pActionDrawRoom);
	m_pActionGroupDraw->addAction(m_pActionDrawRound);
	m_pActionGroupDraw->addAction(m_pActionPutSensor);
	m_pActionGroupDraw->addAction(m_pActionIdle);
	//connect 
    m_pActionClose = new QAction(tr("E&xit"), this);
    connect(m_pActionClose, SIGNAL(triggered()), this, SLOT(close()));

}

void CMainWindow::onDrawSpecial()
{
	m_pDrawSpecialDialog->exec();
	if(m_pDrawSpecialDialog->result() == QDialog::Accepted)
	{
		QPoint qPointFrom = m_pDrawSpecialDialog->from();
		QPoint qPointTo = m_pDrawSpecialDialog->to();
		if (m_qWidgetFloor.isPointInside(qPointFrom) && m_qWidgetFloor.isPointInside(qPointTo))
			m_qWidgetFloor.drawSpecial(qPointFrom, qPointTo, m_pDrawSpecialDialog->type());
		else QMessageBox::critical(this, tr("Error"), tr("Wrong shape dimensions!")); 
	}
}

void CMainWindow::onSettings()
{
	m_pSettingsDialog->exec();
}

void CMainWindow::exportGraphToGraphViz()
{
	QString qStrFileName = QFileDialog::getSaveFileName(this, tr("Save File"), tr("network.dot"), tr("Dot Files (*.dot)"));  
	if (false == qStrFileName.isEmpty())
	{
		CSensorGraph *pSensorGraph = new CSensorGraph(CSensorRepository::inst()->toQList()); 
		pSensorGraph->writeGraphViz(qStrFileName.toLocal8Bit().data());
		SafeFree(pSensorGraph);
	}
}

void CMainWindow::startSimulation()
{
	assert(NULL != m_pPropagationSim);
	if (CSensorRepository::inst()->isEmpty())
	{	 
		if (QMessageBox::question(this, tr("Warning"), 
			tr("There is no sensors on the floor plan!\nDo you still want to continue?"),
			QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
			return;
	}

	// get image of the floorplan
	m_qImageFloor = m_qWidgetFloor.convertToImage();
	m_qWidgetSim.drawImage(m_qImageFloor);
	if (true == m_qWidgetFloor.isValid())
	{
		// run simulation in a separate thread
		m_pPropagationSim->runSimulation(m_qWidgetFloor.hasChanged());
		// display progress
		m_pProgressDlg->exec();
	}
}

void CMainWindow::cancelSimulation()
{
	m_pPropagationSim->cancelSimulation();
	m_qWidgetFloor.setElaborated(false);
}

void CMainWindow::finishSimulation()
{
	m_pProgressDlg->reset();
	// TODO chech this in linux
	m_pProgressDlg->hide();
	m_qWidgetFloor.setElaborated(true);
	// TODO create sensor graph
	m_pQActionExportGraphToGraphViz->setEnabled(true);
}

void CMainWindow::updateSensorList()
{
	m_pQListWidgetSensors->clear();
	m_pQListWidgetSensors->addItems(CSensorRepository::inst()->getQListNames());

}

void CMainWindow::selectSensor(QListWidgetItem *pItem)
{
	assert(NULL != pItem);
	QString qStringText = pItem->text(); 
	// check if list is not empty 
	if (m_pQListWidgetSensors->count() && !qStringText.isEmpty())
	{
		// get sensor from repository
		CSensor *pSensor = CSensorRepository::inst()->getSensor(qStringText.split("ID=").last().toInt());
		assert(NULL != pSensor);
		// check if we need to update the field
		if (NULL == pSensor)
			// TODO: throw an exception here 
			return; 
		// run simulation if needed
		if (false == pSensor->isFieldUpdated() || true == m_qWidgetFloor.hasChanged())
			startSimulation();

		const unsigned char *rgnbyField = pSensor->getField();
		QPoint qPt;
		assert(NULL != rgnbyField);
		if (NULL == rgnbyField)
			// TODO: throw an exception here 
			return; 

		for (qPt.ry() = 0; qPt.ry() < pSensor->fieldHeight(); qPt.ry()++)
			for (qPt.rx() = 0; qPt.rx() < pSensor->fieldWidth(); qPt.rx()++)
			{
				QColor color;
				int byHValue = (int)((*rgnbyField++)*2.4);
				if (byHValue > 255)
					byHValue = 255;
				color.setHsv(byHValue, 255, 255);
				m_qImageFloor.setPixel(qPt, color.rgb());
			}
		// update the widget 
		m_qWidgetSim.drawImage(m_qImageFloor);
		m_qWidgetSim.selectSensor(pSensor);
	}
}

void CMainWindow::createNewFloorPlan()
{
	bool bOk = false;
	int nResolution = CSimProperty::inst()->getResolution();
	int nWidth = m_qWidgetFloor.getWidth()*nResolution;
	int nHeight = m_qWidgetFloor.getHeight()*nResolution;

	while (false == bOk)
	{
		QString strTmp = QString("[%1 X %2], cm").arg(nWidth).arg(nHeight);
		QString strDims = QInputDialog::getText(this, tr("New FloorPlan..."),
			tr("Enter new dimension Width X Height, (cm):"), 
							QLineEdit::Normal, strTmp, &bOk);
		if (false == bOk) 
			return; 
		if (strDims.contains("X", Qt::CaseInsensitive))
		{
   			nWidth = strDims.section("X", 0, 0, 
			QString::SectionCaseInsensitiveSeps).remove(QRegExp("\\D")).toInt(&bOk);  
			if (true != bOk) continue;

			nHeight = strDims.section("X", 1, 1, 
					QString::SectionCaseInsensitiveSeps).remove(QRegExp("\\D")).toInt(&bOk);  
			if (true != bOk) continue;
			
			nWidth /= nResolution;
			nHeight /= nResolution;
			
			if (nHeight < 1 || nHeight > 10000 || nWidth < 1 || nWidth > 10000)
				bOk = false;
		}
	}
	m_qWidgetFloor.resize(nWidth, nHeight);
	m_qWidgetSim.clear();
	m_qWidgetSim.resize(nWidth, nHeight);
	CSensorRepository::inst()->init();
}
/****************************************************************************
* End of file													 			*
****************************************************************************/
